package com.beantools.file;

import com.beantools.constants.SingleChar;
import com.beantools.factory.format.JsonFormat;
import com.beantools.util.poutil.FileUtil;

import java.io.*;

import static com.beantools.constants.Charset.CHARSET;
import static com.beantools.constants.MainASCCode.CHAR_SPACE;
import static com.beantools.constants.SingleChar.DOUBLE_SPACE;
import static com.beantools.constants.SingleChar.LINE_FEED_OP;
import static com.beantools.util.ObjectUtil.isNotNull;
import static com.beantools.util.ObjectUtil.isNull;
import static com.beantools.util.StringUtil.*;

/**
 * 这是操作文件的{@code jsonFile}类。调用者通过该类，把Json字符串
 * 写出到指定文件中。或许，从指定的文件中，读取json文件到程序中。
 *
 * @author baoya
 * @version 1.1.0
 * @since 1.0
 */
public class JsonFile<T, R> extends AbstractFile implements Serializable {

    private static final long serialVersionUID = -8589302784280224900L;

    private static JsonFile instance = null;

    /**
     * 输出的文件的内容
     */
    private String content;


    /**
     * 私有构造器，不允许外部调用者通过构造器创建对象。
     */
    private JsonFile(String content) {
        if (isEmpty(content)) {
            throw new IllegalArgumentException("输出文件内容为空，没有意义content=" + content);
        }
        this.content = content;
    }

    private JsonFile() {

    }

    /**
     * 通过静态方法实例化对象。如果{@code instance}为null，
     * 则手动创建其实例化的对象
     */
    public static JsonFile getInstance(String content) {
        return new JsonFile(content);
    }

    /**
     * 通过静态方法实例化对象。如果{@code instance}为null，
     * 则手动创建其实例化的对象
     */
    public static JsonFile getInstance() {
        return new JsonFile();
    }


    /**
     * 输出json字符串到文件的方法。调用者可以根据绝对路径名，将文件输出到指定
     * 的位置。如果字符串没有传入，默认是输出到当前项目的根路径。
     * <p>
     * 文件清空内容后再导入文件
     * <p>
     * 这里采用格式化输出字符串
     *
     * @param path 文件输出的路径，包括文件名
     * @return 输出成功，则为true，否则，为false
     * @throws IOException 文件异常
     */
    public boolean exportFormatByAbsolutePath(String path) throws IOException {
        return exportFormat(path, null, false);
    }

    /**
     * 输出json字符串到文件的方法。调用者可以根据绝对路径名，将文件输出到指定
     * 的位置。如果字符串没有传入，默认是输出到当前项目的根路径。
     * <p>
     * 这里采用格式化输出字符串
     *
     * @param path 文件输出的路径，包括文件名
     * @return 输出成功，则为true，否则，为false
     * @throws IOException 文件异常
     */
    public boolean clearAndAppendFile(String path) throws IOException {
        if (isBlank(path)) {
            throw new IllegalArgumentException("文件路径为空path=" + path);
        }
        File file = new File(path);
        if (file.exists() && file.isFile()) {
            file.delete();
        }
        content = JsonFormat.format(content);
        return exportJson(file);
    }

    /**
     * 输出json字符串到文件的方法。调用者可以根据相对路径名，将文件输出到指定
     * 的位置。如果字符串没有传入，默认是输出到当前项目的根路径。如果调用者
     * 没有写文件名，则是默认是当前时间的24时制的名字
     * <p>
     * 这里采用格式化输出字符串
     *
     * @param path     文件输出的路径
     * @param fileName 输出的文件名称
     * @return 输出成功，则为true，否则，为false
     * @throws IOException 文件异常
     */
    public boolean exportFormatByRelativePath(String path, String fileName) throws IOException {
        return exportFormat(path, fileName, true);
    }

    /**
     * 输出json字符串到文件的方法。调用者可以根据路径名，将文件输出到指定
     * 的位置。如果字符串没有传入，默认是输出到当前项目的根路径。如果调用者
     * 没有写文件名，则是默认是当前时间的24时制的名字
     * <p>
     * 这里采用是否格式化输出字符串
     *
     * @param path           文件输出的路径
     * @param fileName       输出的文件名称
     * @param isRelativePath 文件是否是绝对路径，
     *                       true表示相对路径，false表示绝对路径
     * @return 输出成功，则为true，否则，为false
     * @throws IOException 文件异常
     */
    public boolean exportFormat(String path, String fileName, boolean isRelativePath) throws IOException {
        content = JsonFormat.format(content);
        path = getRealPath(path, isRelativePath, fileName);
        return exportJson(new File(path));
    }

    /**
     * 输出json字符串到文件的方法。调用者可以根据路径名，将文件输出到指定
     * 的位置。如果字符串没有传入，默认是输出到当前项目的根路径。如果调用者
     * 没有写文件名，则是默认是当前时间的24时制的名字
     * <p>
     * 这里采用是否格式化输出字符串
     *
     * @param file 输出的文件
     * @return 输出成功，则为true，否则，为false
     * @throws IOException 文件异常
     */
    @Override
    public boolean exportJson(File file) throws IOException {
        if (isNull(file)) {
            throw new IllegalArgumentException("文件不能为空");
        }
        FileOutputStream fos = null;
        OutputStreamWriter osw = null;
        PrintWriter out = null;
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            fos = new FileOutputStream(file);
            osw = new OutputStreamWriter(fos, CHARSET);
            out = new PrintWriter(osw);
            char[] bytes = content.toCharArray();
            for (char aChar : bytes) {
//                遇到空字符，添加字符空字符
                if (aChar == CHAR_SPACE) {
                    out.write(DOUBLE_SPACE);
                } else {
                    out.write(aChar);
                }
            }
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            FileUtil.closeOutputStream(fos);
            FileUtil.closeWrite(osw, out);
        }
        return true;
    }


    /**
     * 通过文件路径和文件路径名，从文件当中读取json字符串。调用者时常需要从文件中
     * 读取json字符串。如果该文件存在，就读取该文件，否则，就报出文件不存在的异常。
     * 如果文件的内容为空，则会报出文件异常的错误。
     *
     * @param path 待输入的文件路径
     * @return 输入成功，则为true，否则，为false
     * @throws IOException              文件异常
     * @throws IllegalArgumentException 文件不能为空的异常
     */
    public String importJson(String path, String fileName) throws IOException, IllegalArgumentException {
        if (isAllBlank(path, fileName)) {
            throw new IllegalArgumentException("文件路径或文件名不能为空");
        }
        path = path + SingleChar.INVERSE_LEAN + fileName;
        return importJson(new File(getFilePath(path)));
    }

    /**
     * 通过文件路径，从文件当中读取json字符串。我们时常需要从文件中读取json字符串
     * 如果该文件存在，就读取该文件，否则，就报出文件不存在的异常。
     * 如果文件的内容为空，则会报出文件异常的错误。
     *
     * @param path 待输入的文件路径
     * @return 输入成功，则为true，否则，为false
     * @throws IOException              文件异常
     * @throws IllegalArgumentException 文件不能为空的异常
     */
    public String importJson(String path) throws IOException, IllegalArgumentException {
        if (isBlank(path)) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        return importJson(new File(path));
    }


    /**
     * 从文件当中读取json字符串。我们时常需要从文件中读取json字符串
     * 如果该文件存在，就读取该文件，否则，就报出文件不存在的异常。
     * 如果文件的内容为空，则会报出文件异常的错误。
     *
     * @param file 待输入的文件
     * @return 输入成功，则为true，否则，为false
     * @throws IOException              文件异常
     * @throws IllegalArgumentException 文件不能为空的异常
     */
    public String importJson(File file) throws IOException, IllegalArgumentException {
        if (isNull(file)) {
            throw new IllegalArgumentException("文件不能为空");
        }
        if (!file.exists()) {
            throw new IOException("文件不存在");
        }
        FileInputStream fis = null;
        InputStreamReader osr = null;
        StringBuilder stringBuilder = new StringBuilder();
        try {
            fis = new FileInputStream(file);
            osr = new InputStreamReader(fis, CHARSET);
            int fileLength = (int) file.length();
            char[] chars = new char[fileLength];
            while ((osr.read(chars)) != -1) {
                stringBuilder.append(chars);
                stringBuilder.append(LINE_FEED_OP);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            FileUtil.closeInputStream(fis);
            FileUtil.closeReader(osr);
        }
        return isNotNull(stringBuilder) ? stringBuilder.toString().trim() : null;
    }

}
